use network_scanner_detector_compiler::{
    compile, generate_error_message,
    inkwell::{
        context::Context,
        targets::{CodeModel, InitializationConfig, RelocMode, Target, TargetTriple},
        OptimizationLevel,
    },
    record::RecordHandler,
    LlvmBitcodeBufferTarget, LlvmModuleTarget,
};
use std::{
    error::Error,
    path::{Path, PathBuf},
};
use tokio::fs::{create_dir_all, read_dir, read_to_string};

pub async fn compile_dir<P: AsRef<Path>, T: AsRef<Path>>(
    input: P,
    output: T,
    record_handler: &mut impl RecordHandler,
) -> Result<Vec<PathBuf>, Box<dyn Error>> {
    Target::initialize_bpf(&InitializationConfig::default());
    let target_triple = TargetTriple::create("bpfel-unknown-none");
    let target = Target::from_triple(&target_triple).unwrap();
    let target_machine = target
        .create_target_machine(
            &target_triple,
            "generic",
            "",
            OptimizationLevel::None,
            RelocMode::Default,
            CodeModel::Default,
        )
        .unwrap();
    let context = Context::create();
    let output_dir = PathBuf::from(output.as_ref());
    create_dir_all(&output_dir).await?;
    let mut dir = read_dir(input).await?;
    let mut output_files = Vec::<PathBuf>::new();
    while let Some(file) = dir.next_entry().await? {
        if !file.file_type().await?.is_file() {
            continue;
        }
        let path = file.path();
        let Some(filename) = path.file_stem() else {
            continue;
        };
        let filename = filename.to_str().expect("Invalid file name");
        let output_filename = output_dir.join(filename.to_string() + ".bc");
        let compile_target = LlvmModuleTarget::new(&context, &target_machine);
        let mut compile_target = LlvmBitcodeBufferTarget::new(&compile_target);
        let content = read_to_string(&path).await?;
        let bitcode_buff = match compile(
            &mut compile_target,
            content.as_str(),
            filename,
            record_handler,
        ) {
            Ok(bitcode) => bitcode,
            Err(err) => panic!("{}", generate_error_message(&content, err)),
        };
        std::fs::write(&output_filename, bitcode_buff.as_slice())?;
        output_files.push(output_filename);
    }
    Ok(output_files)
}
